import asyncio
import time

import aiohttp
import pandas as pd
import requests
from os.path import expanduser
import hysdata.api as api
from hysdata.models import Query, HSException
from hysdata.store import Store
import numpy as np
import datetime as dt
from hysdata.utils import load_config, store_config

home = expanduser("~").replace("\\", "/")


class HSClient(object):
    def __init__(self, token: str = None, auto_wait: bool = True, speed_limit_per_minute=120):
        self.speed_limit_dict = {}  # 分钟:{api:count}
        self.speed_limit_per_minute = speed_limit_per_minute
        self.auto_wait = auto_wait
        config = load_config()
        if token is None or len(token) < 1:
            self.token = config.get("token")
            if self.token is None:
                print("本地没有找到token缓存,请输入token!")
        else:
            self.token = token
            config['token'] = token
            store_config(config)
        self.headers = {
            "Content-Type": "application/json",
            "Application-Token": self.token
        }
        self.base_url = "https://udata.hs.net/udata/business/v1/app_services"

    def need_wait(self, query=None, module=None, method=None):
        """
        参数 query 或者 module+method
        返回 这个接口调用要等待的时间
        """
        if query is not None:
            module = query.module
            method = query.method
        cur = dt.datetime.now()
        minute = cur.strftime("%Y%m%d%H%M")
        key = module + method
        if minute in self.speed_limit_dict.keys() and key in self.speed_limit_dict[minute].keys() and \
                self.speed_limit_dict[minute][key] > self.speed_limit_per_minute - 5:
            return (pd.to_datetime((cur + dt.timedelta(minutes=1)).strftime("%Y-%m-%d %H:%M:00")) - cur).seconds
        else:
            return 0

    async def async_auto_wait(self, query=None, module=None, method=None):
        need_wait_seconds = self.need_wait(query, module, method)
        if need_wait_seconds > 0:
            await asyncio.sleep(need_wait_seconds)

    def sync_auto_wait(self, query=None, module=None, method=None):
        need_wait_seconds = self.need_wait(query, module, method)
        if need_wait_seconds > 0:
            time.sleep(need_wait_seconds)

    async def async_call(self, module, method, **kwargs) -> pd.DataFrame:
        if self.auto_wait:
            self.mark_request_count(module, method)
        base_url = self.base_url
        async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False, ttl_dns_cache=30)) as session:
            url = "".join((base_url, "/", module, "/", method))
            async with session.post(url, json=kwargs,
                                    headers=self.headers) as response:
                rsp = await response.json()
                if response.status == 200:
                    if "error_code" in rsp:
                        if int(rsp["error_code"]) == 0:
                            return rsp["data"]
                        else:
                            raise HSException(method, kwargs, response.status_code, rsp['error_code'],
                                              rsp['error_info'])
                else:
                    raise HSException(method, kwargs, response.status_code, rsp['error_code'], rsp['error_info'])

    async def async_query(self, query: Query, cn_column_names=False, change_types=False) -> pd.DataFrame:
        try:
            data = await self.async_call(query.module, query.method, **query.params)
            df = pd.DataFrame(data) if data else pd.DataFrame()
            df.columns = df.columns.map(lambda x: x.lower())
            if df.shape[0] > 0:
                return utils.change_type_and_column_names(df, query, cn_column_names, change_types=change_types)
            else:
                return df
        except Exception as e:
            raise e

    def sync_call(self, module, method, **kwargs) -> dict:
        if self.auto_wait:
            self.mark_request_count(module, method)
        url = "".join([self.base_url, "/", module, "/", method])
        response = requests.post(url, headers=self.headers, json=kwargs)
        rsp = response.json()
        if response.status_code == 200:
            if "error_code" in rsp:
                if int(rsp["error_code"]) == 0:
                    return rsp["data"]
                else:
                    raise HSException(method, kwargs, response.status_code, rsp['error_code'], rsp['error_info'])
        else:
            raise HSException(method, kwargs, response.status_code, rsp['error_code'], rsp['error_info'])

    def sync_query(self, query: Query, cn_column_names=False, change_types=False) -> pd.DataFrame:
        try:
            data = self.sync_call(query.module, query.method, **query.params)
            df = pd.DataFrame(data) if data else pd.DataFrame()
            df.columns = df.columns.map(lambda x: x.lower())
            if df.shape[0] > 0:
                return utils.change_type_and_column_names(df, query, cn_column_names, change_types=change_types)
            else:
                return df
        except Exception as e:
            raise e

    def mark_request_count(self, module, method):
        """
        标记每分钟每个接口调用了多少次
        """
        key = module + method
        cur = dt.datetime.now()
        minute = cur.strftime("%Y%m%d%H%M")
        if minute not in self.speed_limit_dict.keys():
            self.speed_limit_dict[minute] = {key: 1}
        else:
            count_dict = self.speed_limit_dict[minute]
            if key in count_dict.keys():
                self.speed_limit_dict[minute][key] = self.speed_limit_dict[minute][key] + 1
            else:
                self.speed_limit_dict[minute][key] = 1
        for i in range(1, 5, 1):
            pre = (cur - dt.timedelta(minutes=i)).strftime("%Y%m%d%H%M")
            if pre in self.speed_limit_dict.keys():
                del self.speed_limit_dict[pre]
        return 0


__all__ = ['HSClient', 'api', 'Store']

if __name__ == "__main__":
    # client = HSClient()
    # df = client.sync_query(
    #     api.con_calender(prompt_type=4), cn_column_names=True)
    # print(df.T)
    # df = api.all_api("股票")
    # print(df.to_html(render_links=True, escape=False))
    pass
